package com.ray.com.ray.demo.aop;

/**
 * Created by alisports-it06 on 2016/12/22.
 */

import com.ray.com.ray.demo.exception.BizException;
import com.ray.com.ray.demo.exception.ServiceException;
import com.ray.com.ray.demo.utils.ReflexUtil;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;

@Aspect
public class LogPrinterAspectJ {

    private final Log logger = LogFactory.getLog(this.getClass());

    /**
     * Pointcut
     * 定义Pointcut，Pointcut名称为aspectjMethod,必须无参，无返回值
     * 只是一个标识，并不进行调用
     */
   // @Pointcut("execution(* com.ray.com.ray.demo.impl.*.*(..))")
    //@Around ( value = "@annotation(com.ray.com.ray.demo.aop.LogPrinter)"  )
   // private void aspectJMethod(){};

    //@Before("aspectJMethod()") 方法执行前
    @Before(value="@annotation(com.ray.com.ray.demo.aop.LogPrinter)")
    public void doBefore(JoinPoint joinPoint)  throws Throwable {
        Object[] args = joinPoint.getArgs();
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        String methodName = joinPoint.getSignature().getName();
        String[] paramNames = ReflexUtil.getFieldsName(this.getClass(), clazzName, methodName);

        Class[] methodsArgs = new Class[args.length];
        Class<?> clz = joinPoint.getTarget().getClass();
        for (int i$ = 0 ; i$ < args.length ; methodsArgs[i$]= args[i$].getClass(),i$ ++ ) ;
        Method method =	clz.getMethod(methodName, methodsArgs);
        LogPrinter logAnnotation = method.getAnnotation(LogPrinter.class);

        String paramStr = ReflexUtil.getParamStr(paramNames, joinPoint, logAnnotation.printCollection());
        String infoMsg = MessageFormat.format("req start! className={0},methodName={1},params={2},moniterCode={3},desc={4}",
                new Object[]{clazzName, methodName, paramStr, logAnnotation.moniterCode(), logAnnotation.desc()});
        logger.info(infoMsg);
    }

    //@Around("aspectJMethod()") 执行后
    @Around (value="@annotation(com.ray.com.ray.demo.aop.LogPrinter)")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        long startTime = System.currentTimeMillis();
        //核心逻辑
        Object retval = pjp.proceed();
        long costTime = System.currentTimeMillis() - startTime;
        String methodName = pjp.getSignature().getName();
        //记录执行请求耗时
        String rspStr = ReflectionToStringBuilder.toString(retval, ToStringStyle.DEFAULT_STYLE);
        String infoMsg = MessageFormat.format("{0} processed! Cost={1}ms, retval={2}.",new Object[]{methodName, costTime, rspStr});
        logger.info(infoMsg);
        return retval;
    }

    //@AfterThrowing(value="aspectJMethod()", throwing="e") 抛异常时
    @AfterThrowing(value="@annotation(com.ray.com.ray.demo.aop.LogPrinter)", throwing="e")
    public void doThrowing(JoinPoint joinPoint,Exception e){
        /**
         * 此处意在执行核心业务逻辑出错时，捕获异常，并可做一些日志记录操作等
         */
        logger.error("-----doThrowing()开始-----");
        logger.error(" 错误信息："+e.getMessage());
        if(e instanceof BizException){
            BizException bizError = (BizException)e;
            logger.error("biz error, code="+bizError.getErrorCode()+",msg="+bizError.getErrorMsg());
        }
        if(e instanceof ServiceException){
            ServiceException serError = (ServiceException)e;
            logger.error("service error, code="+serError.getErrorCode()+",msg="+serError.getErrorMsg());
        }
    }


}
